// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;

namespace Microsoft.AspNetCore.SpaProxy
{
    /// <summary>
    /// Middleware to display a page while the SPA proxy is launching and redirect to the proxy url once the proxy is
    /// ready or we have given up trying to start it.
    /// This is to help Visual Studio work well in several scenarios by allowing VS to:
    /// 1) Launch on the URL configured for the backend (we handle the redirect to the proxy when ready).
    /// 2) Ensure that the server is up and running quickly instead of waiting for the proxy to be ready to start the
    ///    server which causes Visual Studio to think the app failed to launch.
    /// </summary>
    internal class SpaProxyMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly SpaProxyLaunchManager _spaProxyLaunchManager;
        private readonly IOptions<SpaDevelopmentServerOptions> _options;
        private readonly IHostApplicationLifetime _hostLifetime;
        private readonly ILogger<SpaProxyMiddleware> _logger;

        public SpaProxyMiddleware(
            RequestDelegate next,
            SpaProxyLaunchManager spaProxyLaunchManager,
            IOptions<SpaDevelopmentServerOptions> options,
            IHostApplicationLifetime hostLifetime,
            ILogger<SpaProxyMiddleware> logger)
        {
            _next = next ?? throw new ArgumentNullException(nameof(next));
            _spaProxyLaunchManager = spaProxyLaunchManager ?? throw new ArgumentNullException(nameof(spaProxyLaunchManager));
            _options = options ?? throw new ArgumentNullException(nameof(options));
            _hostLifetime = hostLifetime ?? throw new ArgumentNullException(nameof(hostLifetime));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path.Equals(new Uri(_options.Value.ServerUrl).LocalPath))
            {
                context.Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0";
                if (!await _spaProxyLaunchManager.IsSpaProxyRunning(context.RequestAborted))
                {
                    _spaProxyLaunchManager.StartInBackground(_hostLifetime.ApplicationStopping);
                    _logger.LogInformation("SPA proxy is not ready. Returning temporary landing page.");
                    context.Response.ContentType = "text/html";

                    await using var writer = new StreamWriter(context.Response.Body, Encoding.UTF8);
                    await writer.WriteAsync(GenerateSpaLaunchPage(_options.Value));
                }
                else
                {
                    _logger.LogInformation($"SPA proxy is ready. Redirecting to {_options.Value.ServerUrl}.");
                    context.Response.Redirect(_options.Value.ServerUrl);
                }
            }
            else
            {
                await _next(context);
            }

            string GenerateSpaLaunchPage(SpaDevelopmentServerOptions options)
            {
                return $@"
<!DOCTYPE html>
<html lang=""en"">
<head>
  <meta charset = ""UTF-8"" >
  <meta http-equiv=""X-UA-Compatible"" content=""IE=edge"">
  <meta http-equiv=""refresh"" content=""3"">
  <meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
  <title>SPA proxy launch page</title>
</head>
<body>
  <h1>Launching the SPA proxy...</h1>
  <p>This page will automatically redirect to <a href=""{HtmlEncoder.Default.Encode(options.ServerUrl)}"">{HtmlEncoder.Default.Encode(options.ServerUrl)}</a> when the SPA proxy is ready.</p>
</body>
</html>";
            }
        }
    }
}
